﻿namespace Eucritta
{
    #region Namespaces
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using ODataValidator.RuleEngine;
    using ODataValidator.RuleEngine.Common;
    #endregion

    class Program
    {
        static void Usage()
        {
            Console.WriteLine("Eucritta [-m|--maxsize maxSizeInBytes] [-f|--format formatHint] [-v|--minDSV minDsvValue] [-u|--username userName] [-p|--password password] <target-service-root-uri>");
            Console.WriteLine("target-service-root-uri: URL pointing to a live OData service document resource.");
            Console.WriteLine("formatHint: either be atompub or json. By default is atompub.");
            Console.WriteLine("maxSize: maximum size of payload to fetched in bytes. By default is 1000000.");
            Console.WriteLine("minDsvValue: request header value of minDataServiceVersion sent out as part of request. Valida values include 1.0, 2.0, 3.0. By default no such header is sent out.");
            Console.WriteLine("userName: username to access the target OData endpoint if authentication is required.");
            Console.WriteLine("password: password to access the target OData endpoint if authentication is required.");
            Console.WriteLine("");
            Console.WriteLine("note: Please put extension rules(code rule dlls) to extensions/ follder and xml rules to rulestore/ folder respectively.");
        }

        static void Main(string[] args)
        {
            const string pathXmlRules = "rulestore";
            const string pathCodeRules = "extensions";

            int maxSize = 1000000;
            string format = "atompub";
            string minDsvValue = null;
            string rootUri = null;
            string username = null;
            string password = null;

            try
            {
                for (int i = 0; i < args.Length; i++)
                {
                    switch (args[i])
                    {
                        case "-m":
                        case "--maxsize":
                            maxSize = Int32.Parse(args[++i]);
                            break;
                        case "-f":
                        case "--format":
                            format = args[++i];
                            break;
                        case "-v":
                        case "--minDSV":
                            minDsvValue = args[++i];
                            break;
                        case "-u":
                        case "--username":
                            username = args[++i];
                            break;
                        case "-p":
                        case "--password":
                            password = args[++i];
                            break;
                        default:
                            if (string.IsNullOrEmpty(rootUri))
                            {
                                rootUri = args[i];
                            }
                            else
                            {
                                throw new ArgumentException();
                            }
                            break;
                    }
                }
            }
            catch (Exception)
            {
                Usage();
                return;
            }

            if (string.IsNullOrEmpty(rootUri))
            {
                Usage();
                return;
            }

            if (format != "atompub" && format != "json")
            {
                Usage();
                return;
            }

            Guid rootJobId = Guid.NewGuid();
            ServiceContext ctxLive = null;

            List<KeyValuePair<string, string>> reqHeaders = null;
            if (!string.IsNullOrEmpty(minDsvValue))
            {
                reqHeaders = new List<KeyValuePair<string, string>>();
                reqHeaders.Add(new KeyValuePair<string, string>("minDataServiceVersion", minDsvValue));
            }

            NetworkCredential credential = null;
            if (!string.IsNullOrEmpty(username) || !string.IsNullOrEmpty(password))
            {
                credential = new NetworkCredential(username, password);
            }

            try
            {
                ctxLive = ServiceContextFactory.Create(rootUri, format, rootJobId, maxSize, reqHeaders, credential);
                if (ctxLive.PayloadType != PayloadType.ServiceDoc)
                {
                    Console.WriteLine("{0} is not a OData service document resource.", rootUri);
                    return;
                }
            }
            catch (OversizedPayloadException oex)
            {
                Console.WriteLine("Cannot get the OData resource properly: size exceeded the allowed maximum value.\n\n");
                Console.WriteLine(oex.ToString());
            }

            if (ctxLive != null)
            {
                SimpleJobPlanner jobPlanner = new SimpleJobPlanner(ctxLive, format, maxSize);
                List<KeyValuePair<string, string>> failures;
                var jobs = jobPlanner.GetPlannedJobs(out failures);

                //set up rule engine properly: loads rules
                var xmlRuleStore = new RuleStoreAsXmlFolder(pathXmlRules);
                RuleCatalogCollection.Instance.RegisterRules(xmlRuleStore.GetRules());
                var extensionStore = new ExtensionRuleStore(pathCodeRules);
                RuleCatalogCollection.Instance.RegisterRules(extensionStore.GetRules());

                var reportCollector = new SimpleReport();

                foreach (var job in jobs)
                {
                    reportCollector.PrintJobHeader(job);
                    RuleEngineWrapper facade = new RuleEngineWrapper(job, reportCollector, null);
                    facade.Validate();
                }

                // display the issues if any
                if (failures!= null && failures.Any())
                {
                    Console.WriteLine("\n\nCrawling issues:\n");
                    foreach (var p in failures)
                    {
                        Console.WriteLine("{0}:\n{1}:\n\n", p.Key, p.Value.ToString());
                    }
                }
            }
        }
    }
}
